
import 'dva-model.dart';
import 'dva-reducer.dart';
import 'redux.dart';

typedef RootReducer UseReducerFunction(RootReducer reducer);
typedef EffectFunction UseEffectFunction(EffectFunction effect);

class DvaHook {
  const DvaHook({
    this.onError, 
    this.onAction, 
    this.onStateChange, 
    this.useReducer, 
    this.useEffect, 
    this.extraReducers, 
    this.extraEnhancers,
    this.useRunReducer,
  });
  final ErrorEventFunction? onError;
  final ActionEventFunction? onAction;
  final StateChangeEventFunction? onStateChange;
  final UseReducerFunction? useReducer;
  final UseEffectFunction? useEffect;
  final UseEffectFunction? useRunReducer;
  final Map<String, DvaReducer>? extraReducers;
  final List<StoreEnhancer>? extraEnhancers;

  Map<String, dynamic> get hooks {
    return <String, dynamic>{
      'onError': onError,
      'onAction': onAction,
      'onStateChange': onStateChange,
      'useReducer': useReducer,
      'useEffect': useEffect,
      'extraReducers': extraReducers,
      'extraEnhancers': extraEnhancers,
      'useRunReducer': useRunReducer,
    };
  }
}

class DvaPlugin {
  Map<String, dynamic> hooks = <String, dynamic> {
    'onError': <ErrorEventFunction>[],
    'onAction': <ActionEventFunction>[],
    'onStateChange': <StateChangeEventFunction>[],
    'useReducer': <UseReducerFunction>[],
    'useEffect': <UseEffectFunction>[],
    'extraReducers': <String, DvaReducer>{},
    'extraEnhancers': <StoreEnhancer>[],
    "useRunReducer": <UseRunReducerFunction>[],
  };

  void use(DvaHook dvaHook) {
    for(String key in dvaHook.hooks.keys) {
      apply(key, dvaHook.hooks[key]);
    }
  }

  bool apply(String key, dynamic value) {
    if(value == null) return false;
    if(hooks[key] == null) return false;
    if(hooks[key] is List) {
      if(value is List) {
        (hooks[key] as List).addAll(value);
      } else {
        (hooks[key] as List).add(value);
      }
      return true;
    }
    if(hooks[key] is Map && value is Map) {
      (hooks[key] as Map).addAll(value);
    }
    return false;
  }

  void onError(dynamic error, Map<String, dynamic> context) {
    final ErrorEventFunction _onError = get('onError') as ErrorEventFunction;
    _onError(error, context);
  }

  void onAction(DvaAction action) {
    final ActionEventFunction _onAction = get('onAction') as ActionEventFunction;
    _onAction(action);
  }

  void onStateChange(StoreOfState<dynamic> prevState,DvaAction action) {
    final StateChangeEventFunction _onStateChange = get('onStateChange') as StateChangeEventFunction;
    _onStateChange(prevState, action);
  }

  Map<String, DvaReducer> get extraReducers {
    return get('extraReducers') as Map<String, DvaReducer>;
  }

  RootReducer useReducer(RootReducer reducer) {
    final UseReducerFunction _useReducer = get('useReducer') as UseReducerFunction;
    return _useReducer(reducer);
  }

  EffectFunction useEffect(EffectFunction effect) {
    final UseEffectFunction _useEffect = get('useEffect') as UseEffectFunction;
    return _useEffect(effect);
  }

  StoreCreator storeEnhancer(StoreCreator next) {
    final StoreEnhancer _storeEnhancer = get('extraEnhancers') as StoreEnhancer;
    return _storeEnhancer(next);
  }

  Future<Map<String, StoreOfState<dynamic>>> useRunReducer(Map<String, StoreOfState> prevState, DvaAction action, RunReducerFunction runReducer) async {
    final UseRunReducerFunction _runReducer = get('useRunReducer') as UseRunReducerFunction;
    return await _runReducer(prevState, action, runReducer);
  }

  dynamic get(String key) {
    dynamic hook = hooks[key];
    switch (key) {
      case 'onError':
        void _onError(dynamic error, Map<String, dynamic> context) {
         for(ErrorEventFunction errorEvent in (hook as List<ErrorEventFunction>)) {
           errorEvent(error, context);
         }
        }
        return _onError;
      case 'onAction':
        void _onAction(DvaAction action) {
          for(ActionEventFunction actionEvent in (hook as List<ActionEventFunction>)) {
            actionEvent(action);
          }
        }
        return _onAction;
      case 'onStateChange':
        void _onStateChange(StoreOfState<dynamic> prevState, DvaAction action) {
          for(StateChangeEventFunction stateChange in (hook as List<StateChangeEventFunction>)) {
            stateChange(prevState, action);
          }
        }
        return _onStateChange;
      case 'useReducer':
        RootReducer _useReducer(RootReducer reducer) {
          for(UseReducerFunction useReducer in (hook as List<UseReducerFunction>)) {
            reducer = useReducer(reducer);
          }
          return reducer;
        }
        return _useReducer;
      case 'useEffect':
        EffectFunction _useEffect(EffectFunction effect) {
          for(UseEffectFunction useEffect in (hook as List<UseEffectFunction>)) {
            effect = useEffect(effect);
          }
          return effect;
        }
        return _useEffect;
       case 'useRunReducer':
        Future<Map<String, StoreOfState>> _useRunReducer(Map<String, StoreOfState> prevState, DvaAction action, RunReducerFunction runReducer) async {
          for(UseRunReducerFunction useRunReducer in (hook as List<UseRunReducerFunction>)) {
            prevState = await useRunReducer(prevState, action, runReducer);
          }
          if(hook.length==0) {
            return await runReducer(prevState, action);
          }
          return prevState;
        }
        return _useRunReducer;
      case 'extraReducers':
        return hook as Map<String, DvaReducer>;
      case 'extraEnhancers':
        StoreCreator _storeEnhancer(StoreCreator next) {
          for(StoreEnhancer storeEnhancer in (hook as List<StoreEnhancer>)) {
            next = storeEnhancer(next);
          }
          return next;
        }
        return _storeEnhancer;
      default:
        return null;
    }
  }
}
